home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1994 February: Tool Chest / Dev.CD Feb 94.toast / New System Software Extensions / QuickDraw™ GX v1.0ß2 / Sample Code / Printing Samples / Printer Drivers… / ImageWriter / NewApp.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-09-13  |  53.4 KB  |  1,895 lines  |  [TEXT/MPS ]

  1. /*
  2.     copyright © 1991, 1992 Apple Computer Inc.  All rights reserved.
  3.     Apple Confidential - Do Not Distribute
  4.     
  5.     NewApp.c
  6.     This file contains all new API implementations for the ImageWriter driver.
  7.     
  8.     Modification history
  9.     04/26/93        dmh                Modified for the b1 seed.
  10.     05/29/91        TED                Added manual feed and faster mode support
  11.     04/23/91        Sam Weiss        Changed Inherit to Forward
  12.     3/19/91            TED                New file today
  13.     
  14. */
  15.  
  16. #include "fixmath.h"
  17. #include "toolutils.h"
  18. #include "packages.h"
  19. #include "script.h"
  20. #include "memory.h"
  21.  
  22. #include <PrintingDrivers.h>                // Standard printing includes
  23. #include <PrintingManager.h>
  24. #include <Graphics Routines.h>
  25. #include <graphics libraries.h>
  26. #include <font library.h>
  27. #include <layout routines.h>
  28.  
  29. #include <Exceptions.h>
  30.  
  31. #include "CommonDefines.h"
  32.  
  33. /* ------------------------------------------------------------------------------------    */
  34. /*    INTERNAL DEFINES                                                                    */
  35. /* ------------------------------------------------------------------------------------    */    
  36.  
  37.  
  38. // things this specific driver puts into the DTP config file
  39. #define    kImageWriterConfigType        'ifig'
  40. #define kImageWriterConfigID        (0)            
  41. typedef struct
  42.     {
  43.     Boolean    hasColorRibbon;
  44.     Boolean    hasSheetFeeder;
  45.     } ImageWriterConfigRecord, *ImageWriterConfigPtr, ** ImageWriterConfigHandle;
  46.     
  47. /* Define special characters needed */
  48. #define ESCAPE                (char) 27
  49.  
  50. /* A record to hold a set margins command */
  51. typedef struct SetMarginsRecord
  52.     {
  53.     char        cEscape;                    // ESCAPE character
  54.     char        cCommand;                    // Set Margins command character
  55.     char        cIndentDistance[4];            // number of dots to indent
  56.     } SetMarginsRecord, *SetMarginsPtr;
  57. #define kSetMarginsCommand    (char)'F'        // ImageWriter II uses 'F' for tabbing
  58. #define kSetMarginsSize        6
  59.  
  60. /*     Define a record that can hold one scan line's worth of data, 1280 will
  61.     only happen at 160 dpi. and 14 inch wide paper.   */
  62. typedef struct ScanLineRecord
  63.     {
  64.     char            cColorEscape;                    // ESCAPE character
  65.     char            cSetColorCommand;                // Set color command
  66.     char            cColor;                            // The color
  67.     char            cEscape;                        // ESCAPE character
  68.     char            cCommand;                        // 'enter graphics' command
  69.     char            cLineLength[4];                    // number of dots to print
  70.     char            iTheData[2240];                    // Bits for the data, enough for one line's worth
  71.     } ScanLineRecord, *ScanLinePtr;
  72. #define kGraphicsCommand    (char)'G'        /* graphics printing command */
  73. #define kRepeatGroup        (char)'V'        /* repeat group character */
  74. #define kSetColorCommand     (char)'K'        /* Set color command */
  75. #define kGroupSize            6                /* Size of one group header */
  76.  
  77. #define kScanLineSize         3                /* NOTE: this is just the header size! */
  78.  
  79. #define kStatusCommand        "\033?"            /* request device status/config */
  80.  
  81. //<FF>
  82. /* ------------------------------------------------------------------------------------    */
  83. /*    INTERNAL ROUTINES                                                                */
  84. /* ------------------------------------------------------------------------------------    */
  85. void Long2Dec(long aLong, Ptr emitHere)
  86. /*
  87.     Converts a long into an ASCII string, padded with leading zeros.
  88. */
  89. {    
  90.     char    aString[10];
  91.     short    i, actualWidth, strLength;
  92.     
  93.     NumToString(aLong, aString);
  94.     
  95.     // Get the width of the string, check for being too small
  96.     strLength = aString[0];
  97.     actualWidth = strLength;
  98.     if (actualWidth < 4)
  99.         actualWidth = 4;
  100.         
  101.     // output the string, padding with the requested character
  102.     strLength = actualWidth-strLength;
  103.     for (i = 0; i < actualWidth; ++i)
  104.         {
  105.         *emitHere++ = (i < strLength) 
  106.             ? '0' : aString[(i+1)-(strLength)];
  107.         }
  108.         
  109. } // Long2Dec
  110.  
  111.  
  112. //<FF>
  113. /* ------------------------------------------------------------------------------------    */
  114. Boolean PrinterHasColorRibbon(void)
  115. /*
  116.     Returns true if the config file says that the printer is blessed with a color ribbon,
  117.     false if it is a black and white ribbon.
  118. */
  119. {
  120.     Boolean                        hasColor = true;
  121.     Str32                        deviceName;
  122.     OSErr                        anErr;
  123.     ImageWriterConfigHandle        configHandle;
  124.     
  125.     // if not formatting to a particular device, assume color ribbon, for widest range of colorSpaces
  126.     
  127.     GXGetPrinterName(GXGetJobOutputPrinter(GXGetJob()), deviceName);
  128.  
  129.     if (deviceName[0] != 0)
  130.         {
  131.         // if we are going to a particular device, assume no color, as that is more common
  132.         hasColor = false;
  133.         
  134.         anErr = GXFetchDTPData(deviceName, kImageWriterConfigType, kImageWriterConfigID, &(Handle)configHandle);
  135.         if (anErr == noErr)
  136.             {
  137.             hasColor = (**configHandle).hasColorRibbon;
  138.             DisposHandle((Handle) configHandle);
  139.             }
  140.         }
  141.     
  142.     return(hasColor);
  143.     
  144. } // PrinterHasColorRibbon
  145.  
  146.  
  147. //<FF>
  148. /* ------------------------------------------------------------------------------------    */
  149. gxViewDevice    NewDeviceResolutionViewDevice(void)
  150. /*
  151.     This routine creates a viewDevice and gives it a scale factor in it's mapping
  152.     appropriate for this device (144 dpi in the case of the IW)
  153. */
  154. {
  155.     gxViewDevice        vd;
  156.     
  157.     // create the viewDevices with a fake bitmap
  158.     {
  159.     gxShape        tempBitmap;
  160.     gxBitmap    aBitmap;
  161.     
  162.     aBitmap.pixelSize     = 1;
  163.     aBitmap.rowBytes     = 0;
  164.     aBitmap.width         = 32;
  165.     aBitmap.height         = 32;
  166.     aBitmap.image         = nil;
  167.     aBitmap.space         = gxNoSpace;
  168.     aBitmap.set         = nil;
  169.     aBitmap.profile     = nil;
  170.     
  171.     tempBitmap = GXNewBitmap(&aBitmap, nil);
  172.     vd = GXNewViewDevice(gxScreenViewDevices, tempBitmap);
  173.     GXDisposeShape(tempBitmap);
  174.     }
  175.  
  176.     // setup a mapping for a 144 (2X) viewDevice
  177.     {
  178.         gxMapping    vdMapping;
  179.         
  180.         ResetMapping(&vdMapping);
  181.         ScaleMapping(&vdMapping, ff(2), ff(2), ff(0), ff(0));
  182.         
  183.         GXSetViewDeviceMapping(vd, &vdMapping);
  184.     }
  185.     
  186.     return(vd);
  187.     
  188. } // NewDeviceResolutionViewDevice
  189.  
  190. //<FF>
  191. /* ------------------------------------------------------------------------------------    */
  192. Boolean    JobIsBest(long *imagewriterOptions)
  193. /*
  194.     Returns true if the current job is a final quality mode job, else returns false.
  195.     Also, returns the imagewriter rendering options.
  196. */
  197. {
  198.     Boolean            isFinal;
  199.     gxQualityInfo    jobQualitySettings;
  200.     long            itemSize = sizeof(jobQualitySettings);
  201.     OSErr            status;
  202.     Collection        jobCollection;
  203.     
  204.     // cache the collection
  205.     jobCollection = GXGetJobCollection(GXGetJob());
  206.     
  207.     // find out the info
  208.         
  209.     isFinal = false;
  210.     
  211.     status = GetCollectionItem(jobCollection, 
  212.                                gxQualityTag, gxPrintingTagID, 
  213.                                &itemSize, &jobQualitySettings);
  214.     
  215.     if ( (status == noErr) && (jobQualitySettings.currentQuality == (jobQualitySettings.qualityCount-1)) )
  216.         isFinal = true;
  217.     
  218.     ncheck( status );
  219.  
  220.     // we default to super res
  221.     *imagewriterOptions = kSuperRes;
  222.     itemSize = sizeof(imagewriterOptions);
  223.     status = GetCollectionItem(jobCollection, 
  224.                                DriverCreator, 0, 
  225.                                &itemSize, imagewriterOptions);
  226.                 
  227.     // and return the job quality mode
  228.     return(isFinal);
  229.     
  230. } // JobIsBest
  231.  
  232.  
  233. //<FF>
  234. /* ------------------------------------------------------------------------------------    */
  235. OSErr FetchStatusString(char * statusString)
  236. /*
  237.     Returns in statusString the current status for the printer.  Returns various
  238.     errors from IO package if the printer's status could not be found.
  239. */
  240. {
  241.     OSErr            anErr;
  242.     long            statusLength;                    // status string size
  243.  
  244.  
  245.     // send the query
  246.     statusLength = 8; // max number of characters to get back
  247.     
  248.     anErr = Send_GXGetDeviceStatus(kStatusCommand, 2, statusString, &statusLength, "\p\n");
  249.     nrequire(anErr, Send_GXGetDeviceStatus);
  250.     
  251. // FALL THROUGH EXCEPTION HANDLING    
  252. Send_GXGetDeviceStatus:
  253.     
  254.     return(anErr);
  255.     
  256. } // FetchStatusString
  257.  
  258. //<FF>
  259. /* ------------------------------------------------------------------------------------    */
  260. OSErr UpdateConfiguration(void)
  261. /*
  262.     This routine queries the printer for its hardware configuration (color ribbon and
  263.     sheet feeder options), and stores that info into the configuration file.
  264. */
  265. {
  266.     OSErr                        anErr = noErr;
  267.     ImageWriterConfigHandle        configHandle;
  268.     ImageWriterConfigPtr        configPtr;
  269.     
  270.     // make a handle to hold our configuration information for the printer
  271.     configHandle = (ImageWriterConfigHandle) NewHandle(sizeof(ImageWriterConfigRecord) );
  272.     anErr = MemError();
  273.     nrequire(anErr, NewHandle);
  274.     
  275.     // setup the default for the device
  276.     configPtr = *configHandle;
  277.     configPtr->hasColorRibbon = false;
  278.     configPtr->hasSheetFeeder = false;
  279.     
  280.     {
  281.     char            statusString[8];                // returned string
  282.  
  283.     // query the device
  284.     anErr = FetchStatusString(statusString);
  285.     nrequire(anErr, FetchStatusString);
  286.     
  287.     // and scan the string looking for information
  288.     configPtr = *configHandle;
  289.     if (statusString[4] == 'C')
  290.         configPtr->hasColorRibbon = true;
  291.     if ( (statusString[5] == 'F') || (statusString[4] == 'F') )
  292.         configPtr->hasSheetFeeder = true;
  293.     
  294.     }
  295.     
  296.     // write out the configuration
  297.     {
  298.     Str32    deviceName;
  299.     
  300.     GXGetPrinterName(GXGetJobOutputPrinter(GXGetJob()), deviceName);
  301.     anErr = GXWriteDTPData(deviceName, kImageWriterConfigType, kImageWriterConfigID, (Handle)configHandle);
  302.     }
  303.     
  304.     
  305. // CLEANUP EXCEPTION HANDLING
  306. FetchStatusString:
  307.     DisposHandle((Handle) configHandle);
  308.     
  309. NewHandle:
  310.     return(anErr);
  311.     
  312. } // UpdateConfiguration
  313.  
  314.  
  315. /* ------------------------------------------------------------------------------------    */
  316. OSErr WriteDraftChars(long **draftTable, unsigned char *draftChar, long numChars)
  317. /*
  318.     This routine writes out a single character in the native set of the printer.
  319.     It uses a table that's part of the driver to do the right thing in order to generate this
  320.     character.
  321. */
  322. {
  323.     OSErr        anErr = noErr;
  324.     char        outputChars[20];                // a maximum of 20 characters can be generated
  325.     short        charCount;                    
  326.     
  327.     // For each character in the buffer, determine how to map the character to a draft character
  328.     for (; numChars > 0; --numChars, ++draftChar)
  329.     {
  330.         // No characters yet for this output character
  331.         charCount = 0;
  332.             
  333.         // Only consider characters in the printable range
  334.         if (*draftChar >= 0x20)
  335.         {
  336.             unsigned long    draftControl = (*draftTable)[*draftChar-0x20];    // Fetch native mode long word corresponding to this character
  337.             unsigned char    outChar;
  338.             unsigned char    nationalSet;
  339.             short                i;
  340.             
  341.             // For each word which composes the native mode long word 
  342.             for (i = 1; i >= 0; --i)
  343.             {
  344.                 // Should we send a backspace character (to overstrike)?
  345.                 if ( (draftControl & 0x80000000) != 0 )
  346.                     outputChars[charCount++] = 0x08;
  347.                 
  348.                 outChar = (draftControl >> 16) & 0xFF;
  349.                 if (outChar != 0)
  350.                 {
  351.                     // Determine the national character set to select
  352.                     nationalSet = (draftControl >> 24) & 0xF;    
  353.     
  354.                     //    Is this character in the standard, built-in character set?
  355.                     if (nationalSet == 0)
  356.                     {
  357.                         outputChars[charCount++] = outChar;
  358.                     }
  359.                     else    //    T => Must select a foreign language character set 
  360.                     {
  361.                         outputChars[charCount++] = 0x1B;
  362.                         outputChars[charCount++] = 0x44;
  363.                         outputChars[charCount++] = nationalSet;
  364.                         outputChars[charCount++] = 0x00;
  365.                         outputChars[charCount++] = outChar;
  366.                         outputChars[charCount++] = 0x1B;                // We always switch back to the kAmerican character set
  367.                         outputChars[charCount++] = 0x5A;
  368.                         outputChars[charCount++] = 0x07;
  369.                         outputChars[charCount++] = 0x00;
  370.                     }
  371.                 }
  372.                 
  373.                 // Take the next (low) word and process it (if we're not all done)
  374.                 draftControl <<= 16;
  375.             }    
  376.         }
  377.             
  378.         // If we generated any data, send it out now
  379.         if (charCount > 0)
  380.             anErr = Send_GXBufferData(outputChars, charCount, gxNoBufferOptions);
  381.     }
  382.         
  383.     return(anErr);    
  384.     
  385. } // WriteDraftChars
  386.  
  387. /* ------------------------------------------------------------------------------------    */
  388. OSErr GetPointerThisBig(Ptr *theBuff, long numBytes) 
  389. {
  390.     OSErr        anErr = noErr;
  391.     
  392.     if (*theBuff != nil)
  393.     {
  394.         if ( GetPtrSize(*theBuff) < numBytes )    //    T => Won't be big enough; make a new one
  395.         {
  396.             DisposPtr(*theBuff);
  397.             *theBuff = nil;
  398.         }
  399.     }
  400.  
  401.     if (*theBuff == nil)
  402.     {
  403.         *theBuff = NewPtrClear(numBytes);
  404.         anErr = MemError();
  405.     }
  406.     
  407.     return(anErr);
  408.     
  409. } // GetPointerThisBig
  410.  
  411. /* ------------------------------------------------------------------------------------    */
  412. OSErr GetTextAndPosition(    gxShape            theShape, 
  413.                             Ptr                *theChars, 
  414.                             long            *numChars, 
  415.                             gxPoint            *textPosition)
  416. {
  417.     OSErr        anErr = noErr;
  418.     long        textLength;
  419.     
  420.     // Determine the size of the text data and the position of the text
  421.     textLength = GXGetLayout(theShape, nil, nil, nil, nil, nil, nil, nil, nil, textPosition);
  422.  
  423.     // Make sure we have a buffer pointer large enough to hold all of the data
  424.     
  425.     anErr = GetPointerThisBig(theChars, textLength);
  426.     require(anErr == noErr, CantAllocTextBuff);
  427.     
  428.     // Now we retrieve the text
  429.     GXGetLayout(theShape, *theChars, nil, nil, nil, nil, nil, nil, nil, nil);
  430.     
  431.     // Remember the number of characters in the shape
  432.     *numChars = textLength;
  433.     
  434.  
  435. /******* Clean-up *******/
  436.  
  437. CantAllocTextBuff:
  438.     return(anErr);
  439.     
  440. } // GetTextAndPosition
  441.  
  442. /* ------------------------------------------------------------------------------------    */
  443. OSErr PrintPageInDraftMode(gxShape thePage, gxRasterImageDataHdl imageData)
  444. {
  445.     OSErr                    anErr = noErr;
  446.     long                    i;
  447.     long                    numItems;
  448.     Fixed                    currYPos = ff(0);
  449.     Ptr                        theChars = nil;
  450.     long                    numChars = 0;
  451.     gxPoint                    textPosition;
  452.     Fixed                    oldTextSize = ff(0);
  453.     SpecGlobalsHdl             hGlobals = GetMessageHandlerInstanceContext();
  454.     
  455.     // Since the page picture we need to process is a picture shape that's embedded in
  456.     // thePage (a shape containing one item => a picture), we need to extract the real
  457.     // page picture from thePage.
  458.     
  459.     thePage = GetPictureItem(thePage, 1, nil, nil, nil, nil);
  460.     numItems = GXGetPicture(thePage, nil, nil, nil, nil);
  461.     
  462.     // For each shape within the picture, check its type and process it accordingly
  463.     
  464.     for (i = 1; i <= numItems; ++i)
  465.     {
  466.         gxShape                theShape;
  467.         short                theType;
  468.                 
  469.         theShape = GetPictureItem(thePage, i, nil, nil, nil, nil);
  470.         theType = GXGetShapeType(theShape);
  471.         
  472.         if (theType == gxLayoutType)    //    T => We have a layout shape
  473.         {
  474.             fixed        textSize;
  475.             char        buff[12];
  476.             char        theFace;
  477.             short        cmndBuffSz;
  478.             
  479.             // First determine the style in which we're printing
  480.             
  481.             theFace = GetStyleCommonFace( GXGetShapeStyle(theShape) );
  482.             
  483.             buff[0] = ESCAPE;
  484.             if ( (theFace & bold) != 0 )    //    T => Turn bold facing on
  485.                 buff[1] = '!';
  486.             else                                    //    T => Turn it off
  487.                 buff[1] = '"';
  488.             
  489.             buff[2] = ESCAPE;
  490.             if ( (theFace & underline) != 0 )    //    T => Turn underline facing on
  491.                 buff[3] = 'X';
  492.             else                                            //    T => Turn it off
  493.                 buff[3] = 'Y';
  494.                 
  495.             cmndBuffSz = 4;
  496.             
  497.             // Next determine if we need to change the size of the font being used
  498.             
  499.             textSize = GXGetShapeTextSize(theShape);
  500.             if (textSize != oldTextSize)    //    T => Must issue LQ command to change font size
  501.             {
  502.                 buff[4] = ESCAPE;                //    The first escape command selects black color
  503.                 buff[5] = kSetColorCommand;
  504.                 buff[6] = '0';
  505.                 
  506.                 buff[7] = ESCAPE;                //    The second escape command selects a draft font
  507.                 buff[8] = 'a';
  508.                 buff[9] = '1';
  509.                 
  510.                 buff[10] = ESCAPE;                //    The third escape command selects the character pitch
  511.                 
  512.                 if ( textSize <= ff(10) )    //    T => Select 10 cpi
  513.                 {
  514.                     buff[11] = 'N';
  515.                 }
  516.                 else    //    T => All other sizes get mapped to 12 cpi
  517.                 {
  518.                     buff[11] = 'E';
  519.                 }
  520.                 
  521.                 // Remember the last text size
  522.                 oldTextSize = textSize;    
  523.                 
  524.                 // Adjust the size of the data to be sent to the printer
  525.                 cmndBuffSz += 8;
  526.             }
  527.             // else - no change in font size
  528.             
  529.             // Send the commands to the printer
  530.             anErr = Send_GXBufferData(buff, cmndBuffSz, gxDontSplitBuffer);
  531.             require(anErr == noErr, CantSendFontCmnd);
  532.  
  533.             // Get the ASCII text and the starting position of the data
  534.             anErr = GetTextAndPosition(theShape, &theChars, &numChars, &textPosition);
  535.             require(anErr == noErr, CantGetTextAndPos);
  536.             
  537.             if ( (currYPos != ff(0)) && (currYPos != textPosition.y) )    //    T => Moving to a lower line, finish the last ;line with a CR
  538.             {
  539.                 char        c = 0x0D;
  540.                 
  541.                 anErr = Send_GXBufferData(&c, 1, gxNoBufferOptions);
  542.                 require(anErr == noErr, CantSendCRCmnd);
  543.             }
  544.             
  545.             // Position the print head to the proper location on the page
  546.             {
  547.                 gxMapping            theMapping;
  548.                 short                lineFeedSize;
  549.                 Str255                positionCmndsBuff;
  550.                 unsigned long        bytesInBuff = 0;
  551.                 char                *p;
  552.  
  553.                 GXGetShapeMapping(theShape, &theMapping);
  554.                 MapPoints(&theMapping, (long) 1, &textPosition);    //    Just map the first point
  555.                 
  556.                 // Now position the print head vertically
  557.  
  558.                 lineFeedSize = (textPosition.y - currYPos) >> 16;
  559.                 anErr = Send_GXRasterLineFeed(&lineFeedSize, positionCmndsBuff, &bytesInBuff, imageData);
  560.                 require(anErr == noErr, CantEmitLineFeeds);
  561.                 
  562.                 // Update the current Y position pointer on the page
  563.                 currYPos = textPosition.y;
  564.  
  565.                 // Now position the print head horizontally on the page
  566.  
  567.                 p = &positionCmndsBuff[bytesInBuff];        
  568.                 *p++ = ESCAPE;
  569.                 *p++ = 'F';
  570.                 Long2Dec((*hGlobals)->leftMargin + FixedToInt(textPosition.x), p);    // Convert left margin into ASCII and place it at the start of the scan line
  571.                 
  572.                 // Update the number of bytes in the buffer
  573.                 bytesInBuff += 6;
  574.  
  575.                 // Send the positioning info to the printer
  576.                 anErr = Send_GXBufferData(positionCmndsBuff, bytesInBuff, gxDontSplitBuffer);
  577.                 require(anErr == noErr, CantSendPositionCmnds);
  578.             }
  579.             
  580.             // Now we send the text data to the printer
  581.             anErr = WriteDraftChars((long **) (*hGlobals)->draftTable, theChars, numChars);
  582.             require(anErr == noErr, CantWriteChars);
  583.         }
  584.     }    // for
  585.  
  586.     // Send one last CR to wrap the last line (if there was one)
  587.     {
  588.         char        c = 0x0D;
  589.         
  590.         anErr = Send_GXBufferData(&c, 1, gxNoBufferOptions);
  591.     }
  592.  
  593.  
  594. /******* Clean-up *******/
  595.  
  596. CantWriteChars:
  597. CantSendPositionCmnds:
  598. CantEmitLineFeeds:
  599. CantSendCRCmnd:
  600. CantGetTextAndPos:
  601. CantSendFontCmnd:
  602.     if (theChars != nil)
  603.         DisposPtr(theChars);
  604.                 
  605.     return(anErr);
  606.     
  607. } // PrintPageInDraftMode 
  608.  
  609. //<FF>
  610. /* ------------------------------------------------------------------------------------    */
  611. /*    SPECIFIC DRIVER UNIVERSAL OVERRIDES                                                    */
  612. /* ------------------------------------------------------------------------------------    */
  613. OSErr SD_Initialize (void) 
  614. /*
  615.     The SD_Initalize message is called when a new job is created.  The standard
  616.     thing to do is to allocate and fill out your globals as you see fit.
  617. */
  618. {
  619.  
  620.     SpecGlobalsHdl     hGlobals;
  621.     OSErr             anErr;
  622.         
  623.     // we make our globals
  624.     hGlobals = (SpecGlobalsHdl) NewHandleClear( sizeof(SpecGlobals) );
  625.     anErr = MemError();
  626.  
  627.     // and we save them away
  628.     SetMessageHandlerInstanceContext(hGlobals);
  629.  
  630.     // is everything okay?
  631.     nrequire(anErr, MNewHandleClear);
  632.     
  633.     // no draft table yet    
  634.     (**hGlobals).draftTable = nil;
  635.     
  636.     return(noErr);
  637.     
  638.     
  639. /*-----EXCEPTION HANDLING------*/
  640.  
  641.  
  642. MNewHandleClear:
  643.     return(anErr);
  644.     
  645. } // SD_Initialize
  646.  
  647.  
  648. //<FF>
  649. /* ------------------------------------------------------------------------------------    */
  650. OSErr SD_ShutDown(void) 
  651. /*
  652.     Shutdown is called when the job is done with.  A good thing to do is to get
  653.     rid of any additional storage that is laying around.
  654. */
  655. {
  656.     // clean up our stuff
  657.     SpecGlobalsHdl hGlobals = GetMessageHandlerInstanceContext();
  658.  
  659.     // get rid of the draft table (if we have one);
  660.     DisposHandle((**hGlobals).draftTable);
  661.     
  662.     // we get rid of our storage
  663.     DisposHandle((Handle) hGlobals);
  664.     
  665.     // clear out our globals - to avoid double disposes
  666.     SetMessageHandlerInstanceContext(nil);
  667.  
  668.     return(noErr);
  669.     
  670.     
  671. } // SD_ShutDown
  672.  
  673. //<FF>
  674. /* ------------------------------------------------------------------------------------    */
  675. OSErr    SD_DefaultPrinter(gxPrinter thePrinter)
  676. /*
  677.     This call is made to setup the default printer object.  The job of the
  678.     specific driver is to add in any viewDevices that it wishes applications
  679.     to be able to format specifically for.
  680. */
  681. {
  682.     OSErr            anErr;
  683.     gxViewDevice    vd;
  684.     
  685.     // add the standard viewDevices first
  686.     anErr = Forward_GXDefaultPrinter(thePrinter);
  687.     nrequire(anErr, DefaultPrinter);
  688.     
  689.     // add a 144 b/w viewDevice
  690.     vd = NewDeviceResolutionViewDevice();
  691.     {
  692.     gxSetColor        theColors[2];
  693.     gxSetColor        *pColor;
  694.     gxColorSet        theSet;
  695.     
  696.     pColor = &theColors[0];
  697.     
  698.     pColor->rgb.red = pColor->rgb.green = pColor->rgb.blue = 0xFFFF;
  699.     
  700.     pColor++;
  701.     pColor->rgb.red = pColor->rgb.green = pColor->rgb.blue = 0x0000;
  702.     
  703.     theSet = GXNewColorSet(gxRGBSpace, 2, theColors);
  704.     SetViewDeviceColorSet(vd, theSet);
  705.     GXDisposeColorSet(theSet);
  706.     }
  707.         
  708.     anErr = GXAddPrinterViewDevice(thePrinter, vd);
  709.     nrequire(anErr, FailedAddBWViewDevice);
  710.  
  711.     
  712.     // add a 144 color viewDevice with 8 colors in it
  713.     //
  714.     //    Color        Index        R            G            B
  715.     //    white        0            0xFFFF        0xFFFF        0xFFFF        
  716.     //    yellow        1            0xFFFF        0xFFFF        0x0000
  717.     //    magenta        2            0xFFFF        0x0000        0xFFFF
  718.     //    red            3            0xFFFF        0x0000        0x0000
  719.     //    cyan        4            0x0000        0xFFFF        0xFFFF
  720.     //    green        5            0x0000        0xFFFF        0x0000
  721.     //    blue        6            0x0000        0x0000        0xFFFF
  722.     //    black        7            0x0000        0x0000        0x0000
  723.     
  724.     if (PrinterHasColorRibbon())
  725.         {
  726.         gxSetColor        theColors[8];
  727.         gxSetColor        *pColor;
  728.         gxColorSet        theSet;
  729.         short            idx;
  730.  
  731.         vd = NewDeviceResolutionViewDevice();
  732.         
  733.         pColor = &theColors[0];
  734.         for (idx = 0; idx < 8; ++idx)
  735.             {
  736.             // default the color to black
  737.             pColor->rgb.red = pColor->rgb.green = pColor->rgb.blue = 0x0000;
  738.             
  739.             // and give it componants to go along with this index
  740.             if (idx & 0x04)
  741.                 pColor->rgb.red     = 0xFFFF;
  742.             if (idx & 0x02)
  743.                 pColor->rgb.green     = 0xFFFF;
  744.             if (idx & 0x01)
  745.                 pColor->rgb.blue     = 0xFFFF;
  746.                 
  747.             // move on to the next color
  748.             ++pColor;
  749.             }
  750.         
  751.         theSet = GXNewColorSet(gxRGBSpace, 8, theColors);
  752.         SetViewDeviceColorSet(vd, theSet);
  753.         GXDisposeColorSet(theSet);
  754.  
  755.         anErr = GXAddPrinterViewDevice(thePrinter, vd);
  756.         nrequire(anErr, FailedAddColorViewDevice);
  757.         }
  758.     
  759.     return(noErr);
  760.     
  761.     
  762.     
  763. // EXCEPTION HANDLING
  764. FailedAddColorViewDevice:
  765. FailedAddBWViewDevice:
  766.     GXDisposeViewDevice(vd);
  767.     
  768. DefaultPrinter:
  769.     return(anErr);
  770.     
  771. } // SD_DefaultPrinter
  772.  
  773. //<FF>
  774. /* ------------------------------------------------------------------------------------    */
  775. OSErr SD_DefaultJob()
  776. /*
  777.     We override this message to add our default - highest res possible
  778. */
  779. {
  780.     OSErr    anErr;
  781.     
  782.     anErr = Forward_GXDefaultJob();
  783.     if (anErr == noErr)
  784.         {
  785.         long        imagewriterOptions = kSuperRes;
  786.         
  787.         anErr = AddCollectionItem(GXGetJobCollection(GXGetJob()), 
  788.                     DriverCreator,
  789.                     0,
  790.                     sizeof(imagewriterOptions),
  791.                     &imagewriterOptions);
  792.                     
  793.         if (anErr == noErr)
  794.             {
  795.             // If the app. supports job format modes, then see if text is supported. If it's, then
  796.             // we force text to be the preferred format mode.
  797.                 gxJobFormatModeTableHdl    theJobFormatModeList;
  798.                 
  799.                 // Did the application specify any job format modes?
  800.                 
  801.                 anErr = GXGetAvailableJobFormatModes(&theJobFormatModeList);
  802.                 if ( (anErr == noErr) && (theJobFormatModeList != nil) )
  803.                     {
  804.                         long    i;
  805.                 
  806.                         // Examine each job format mode to determine if 'text' mode is supported.  If
  807.                         // it is, then we make it the preferred format mode for Direct Mode printing
  808.                         
  809.                         for (i = 0; i <= (*theJobFormatModeList)->numModes - 1; ++i) 
  810.                         {
  811.                             if ((*theJobFormatModeList)->modes[i] == gxTextJobFormatMode) 
  812.                             {
  813.                                 // Tell Printing Manger we support 'text' Direct Mode, but that we also
  814.                                 // support other modes.
  815.                                 
  816.                                 GXSetPreferredJobFormatMode(gxTextJobFormatMode, false);
  817.                                 break;
  818.                             }
  819.                         }
  820.                         
  821.                         // Dump the temporary handle we were issued
  822.                         DisposHandle((Handle)theJobFormatModeList);
  823.                     }
  824.                     
  825.                 // no job format modes specified?  That's okay
  826.                 if (anErr == collectionItemNotFoundErr)
  827.                     anErr = noErr;
  828.             }
  829.         }
  830.  
  831.  
  832.     return(anErr);
  833.     
  834. } // SD_DefaultJob
  835.  
  836. /* ------------------------------------------------------------------------------------    */
  837. OSErr SD_OpenConnection(void)
  838. /*
  839.     The OpenConnection message is sent in order to open the connection to the device.
  840. */
  841. {
  842.     OSErr    anErr;
  843.     
  844.     
  845.     // first, open the connection the standard way
  846.     anErr = Forward_GXOpenConnection();
  847.     nrequire(anErr, OpenConnection);
  848.     
  849.     // then, bring the configuration file up to date
  850.     anErr = UpdateConfiguration();
  851.     nrequire(anErr, UpdateConfiguration);
  852.         
  853.     return(noErr);
  854.     
  855. // EXCEPTION HANDLING
  856. UpdateConfiguration:
  857.     GXCleanupOpenConnection();
  858.     
  859. OpenConnection:
  860.  
  861.     return(anErr);
  862.     
  863. } // SD_OpenConnection
  864.  
  865. /* ------------------------------------------------------------------------------------    */
  866. OSErr SD_StartSendPage(gxFormat pageFormat)
  867. /*
  868.     The StartSendPage message is sent just before the page begins to be rendered.
  869.     
  870.     Note that the StartSendPage message will not be sent until imaging/communication
  871.     time, so that user interaction alerts are considered okay here
  872. */
  873. {
  874.     OSErr                        anErr = noErr;
  875.     gxJob                        theJob = GXGetJob();
  876.     Collection                    jobCollection;
  877.     gxPaperFeedInfo                paperFeed;
  878.     long                        itemSize = sizeof(paperFeed);
  879.  
  880.     check(theJob);
  881.     jobCollection = GXGetJobCollection(theJob);
  882.     check(jobCollection);
  883.     
  884.     // first, check to see if the entire job is auto feed.
  885.     paperFeed.autoFeed = true;
  886.     (void) GetCollectionItem(jobCollection, gxPaperFeedTag, gxPrintingTagID, &itemSize, &paperFeed);
  887.             
  888.     // next, check to see if this particular page is to be manually fed
  889.     if (!paperFeed.autoFeed)
  890.         {
  891.         // we now have the actual size of the paper feed item, so we get the manual feed list
  892.         gxManualFeedInfo    **feedHandle;
  893.         
  894.         feedHandle = (gxManualFeedInfo**) NewHandle(0);
  895.         anErr = MemError();
  896.         nrequire(anErr, FailedNewHandle);
  897.         
  898.         anErr = GetCollectionItemHdl(jobCollection, gxManualFeedTag, gxPrintingTagID, (Handle)feedHandle);
  899.         if (anErr == noErr)
  900.             {
  901.             Str31                paperName;
  902.             short                idx;
  903.             gxManualFeedInfo    *pFeed;
  904.             
  905.             // name of this page's paper type
  906.             GXGetPaperTypeName(GXGetFormatPaperType(pageFormat), paperName);
  907.             
  908.             // assume auto feed
  909.             paperFeed.autoFeed = true;
  910.             
  911.             // lock and dereference for the loop
  912.             HLockHi((Handle) feedHandle);
  913.             pFeed = *feedHandle;
  914.             
  915.             // loop and see if one of our manual feed paperType's name matches
  916.             for (idx = 0; idx < pFeed->numPaperTypeNames; ++idx) 
  917.                 {
  918.                 Ptr pName = &pFeed->paperTypeNames[idx];
  919.                 
  920.                 if ( IUMagIDString(    paperName, 
  921.                                     pName,
  922.                                     paperName[0] + 1,
  923.                                     *pName + 1)     == 0  ) 
  924.                     {
  925.                     paperFeed.autoFeed = false;
  926.                     break;
  927.                     }
  928.                 }
  929.             }
  930.         
  931.         DisposHandle((Handle) feedHandle);
  932.         FailedNewHandle:
  933.             ;
  934.         }
  935.         
  936.     // manual feed?  Time to ask the user what to do
  937.     if (!paperFeed.autoFeed)
  938.         {
  939.         // Wait for all IO to complete, so that we can correctly tell the user what to do.
  940.         // Since the WriteData message makes sure all data is flushed before performing the
  941.         // IO, this call insures that pending IO is complete.
  942.         anErr = Send_GXWriteData(nil, 0);
  943.         nrequire(anErr, FlushAllData);
  944.  
  945.  
  946.         // then, conduct the alert with the user
  947.         {
  948.         gxStatusRecord*        pStat;
  949.         gxManualFeedRecord    *pFeed;
  950.         
  951.         // make a status record containing the request to the user
  952.         pStat = (gxStatusRecord *)NewPtrClear(sizeof(gxStatusRecord)  + sizeof(gxManualFeedRecord));
  953.         anErr = MemError();
  954.         nrequire(anErr, NewPtrClear);
  955.         
  956.         pStat->statResId     = gxUnivAlertStatusResourceId;    // we use the built-in status for this
  957.         pStat->statResIndex = gxUnivManualFeedIndex;            // status meaning "manual feed alert"
  958.         pStat->dialogResult = nil;
  959.         pStat->bufferLen      = sizeof(gxManualFeedRecord);
  960.         pFeed = (gxManualFeedRecord*)&pStat->statusBuffer;
  961.         
  962.         // we can switch to autofeed if we want - and tell the user what kind of paper to load in
  963.         pFeed->canAutoFeed = true;
  964.         GXGetPaperTypeName(GXGetFormatPaperType(pageFormat), pFeed->paperTypeName);
  965.         
  966.         // keep sending the user the alert until either
  967.         //  a) the problem resolves itself
  968.         //  b) the user responds via the dialog
  969.         //  c) some other (fatal) error happens
  970.         do
  971.             {
  972.             
  973.             // tell the user
  974.             anErr = GXAlertTheUser(pStat);
  975.             
  976.             // here one would check to see if the problem went away for printers
  977.             // than can tell such things
  978.             
  979.             } while ((anErr == noErr) && (pStat->dialogResult == nil));
  980.  
  981.         // based on the user's response, continue, cancel, or switch to auto feed
  982.         switch ( pStat->dialogResult )
  983.             {
  984.             case ok:
  985.                 // paper is loaded
  986.                 break;
  987.                 
  988.             case cancel:
  989.                 // user wishes to stop the printing process
  990.                 anErr = gxPrUserAbortErr;
  991.                 break;
  992.                 
  993.             case gxAutoFeedButtonId:
  994.                 // do rest of job with auto feed
  995.                 paperFeed.autoFeed = true;
  996.                 (void) AddCollectionItem(jobCollection, gxPaperFeedTag, gxPrintingTagID, itemSize, &paperFeed);
  997.                 break;
  998.                 
  999.             } // switch
  1000.             
  1001.             
  1002.         // done with the status now
  1003.         DisposPtr((Ptr) pStat);
  1004.         }
  1005.             
  1006.         } // if manual feed job
  1007.         
  1008.         
  1009.     // display "sending data to the printer" message
  1010.     if (anErr == noErr)
  1011.         anErr = GXReportStatus(kDriverStatus, kSendingData);
  1012.         
  1013.     nrequire(anErr, FailedWaitForPaper);
  1014.         
  1015.     // continue with the standard starting of the page
  1016.     anErr = Forward_GXStartSendPage(pageFormat);
  1017.         
  1018.         
  1019. // FALL THROUGH AND HANDLE EXCEPTIONS
  1020.  
  1021. FailedWaitForPaper:
  1022. NewPtrClear:
  1023. FlushAllData:
  1024.     return(anErr);
  1025.     
  1026. } // SD_StartSendPage
  1027.  
  1028. /* ------------------------------------------------------------------------------------    */
  1029. OSErr SD_JobDefaultFormatDialog(gxDialogResult *theResult)
  1030. /*
  1031.     This message is sent in response to the user's request to put up a formatting dialog
  1032. */
  1033. {
  1034.     OSErr anErr;
  1035.     gxJobFormatModeTableHdl    theJobFormatModeList;
  1036.     long    i;
  1037.     gxJob theJob = GXGetJob();
  1038.     
  1039.     // set up the JobFormatMode information
  1040.     
  1041.     anErr = GXGetAvailableJobFormatModes(&theJobFormatModeList);
  1042.     if ((!anErr) && (theJobFormatModeList))
  1043.         {
  1044.         for (i = 0; i <= (*theJobFormatModeList)->numModes - 1; ++i) 
  1045.             {
  1046.             if ((*theJobFormatModeList)->modes[i] == gxTextJobFormatMode) 
  1047.                 {
  1048.                 GXSetPreferredJobFormatMode(gxTextJobFormatMode, false);
  1049.                 break;
  1050.                 }
  1051.             }
  1052.         DisposHandle((Handle)theJobFormatModeList);
  1053.         }
  1054.         
  1055.     // do the normal dialogs after handling the job format mode stuff
  1056.     return(Forward_GXJobDefaultFormatDialog(theResult));
  1057.     
  1058. } // SD_JobDefaultFormatDialog
  1059.  
  1060. /* ------------------------------------------------------------------------------------    */
  1061. OSErr SD_JobFormatModeQuery(    gxQueryType        theQuery,
  1062.                                 void*            srcData,
  1063.                                 void*            dstData)
  1064. /*
  1065.     This message is sent to find out information about the current job format mode.
  1066. */
  1067. {
  1068.     OSErr    anErr = noErr;
  1069.     Handle    theFonts;
  1070.     Handle    theStyles;
  1071.     
  1072.     check(dstData != nil);
  1073.     
  1074.     // What type of query is being requested?
  1075.     switch(theQuery) 
  1076.     {
  1077.         case gxSetStyleJobFormatCommonStyleQuery:
  1078.         {
  1079.             char                *pStyleName;
  1080.  
  1081.             // Fetch the list of supported styles
  1082.             
  1083.             anErr = Send_GXFetchTaggedDriverData('STR#', kFormatModeStylesID, &theStyles);
  1084.             require(anErr == noErr, FailedToLoadStyles1);
  1085.             
  1086.             HNoPurge(theStyles);
  1087.             HLock(theStyles);
  1088.             
  1089.             // Determine which style is being referenced and set the corresponding style (only 2 styles
  1090.             // are currently supported)
  1091.             
  1092.             if (**((short **) theStyles) == 2)    //    T => We have the correct number of styles
  1093.             {
  1094.                 char        whichFace = 0;
  1095.                 
  1096.                 pStyleName = ((char *) *theStyles) + sizeof(short); 
  1097.                 
  1098.                 if ( IUCompString(pStyleName, (char *) srcData) == 0 )    //    T => They want bold face
  1099.                 {
  1100.                     whichFace = bold;
  1101.                 }
  1102.                 else
  1103.                 {
  1104.                     // Point to the next name in the list
  1105.                     pStyleName += *pStyleName + 1;
  1106.  
  1107.                     if ( IUCompString(pStyleName, (char *) srcData) == 0 )    //    T => They want underline face
  1108.                     {
  1109.                         whichFace = underline;
  1110.                     }
  1111.                 }
  1112.  
  1113.                 //    If the client specified a valid face, set it now
  1114.                 if (whichFace != 0)
  1115.                 {
  1116.                     SetStyleCommonFace((gxStyle) dstData, GetStyleCommonFace((gxStyle) dstData) | whichFace);
  1117.                 }
  1118.             }
  1119.             // else - something is wrong with our resource
  1120.             
  1121.             // Dump the temporary handle
  1122.             DisposHandle(theStyles);
  1123.             
  1124.             break;
  1125.         }
  1126.             
  1127.         case gxGetJobFormatFontCommonStylesQuery:
  1128.         {
  1129.             short                numStyles;
  1130.             short                i;
  1131.             char                *pStyleName;
  1132.  
  1133.             // Fetch the list of supported styles
  1134.             
  1135.             anErr = Send_GXFetchTaggedDriverData('STR#', kFormatModeStylesID, &theStyles);
  1136.             require(anErr == noErr, FailedToLoadStyles2);
  1137.             
  1138.             HNoPurge(theStyles);
  1139.             HLock(theStyles);
  1140.             
  1141.             // Determine the number of styles in the list
  1142.             numStyles = **((short **) theStyles);
  1143.  
  1144.             if (*(Handle *)dstData != nil)    //    T => Resize the handle to hold info. on the styles
  1145.                 SetHandleSize(*(Handle *)dstData, sizeof(gxStyleNameTable) + ((numStyles - 1) * sizeof(Str255)));
  1146.             else
  1147.                 *(Handle *)dstData = NewHandle(sizeof(gxStyleNameTable) + ((numStyles - 1) * sizeof(Str255)));
  1148.             
  1149.             anErr = MemError();
  1150.             require(anErr == noErr, StyleTableResizeFailed);
  1151.             
  1152.             // Now extract the name of each of the supported fonts
  1153.             
  1154.             for (i = 1, pStyleName = ((char *) *theStyles) + sizeof(short); i <= numStyles; ++i, pStyleName += *pStyleName + 1)
  1155.             {
  1156.                 BlockMove(pStyleName, (*((gxStyleNameTableHdl) *(Handle *)dstData))->styleNames[i - 1], *pStyleName + 1);
  1157.             }
  1158.             
  1159.             (*((gxStyleNameTableHdl) *(Handle *)dstData))->numStyleNames = numStyles;
  1160.             
  1161.             // Dump the temporary handle
  1162.             DisposHandle(theStyles);
  1163.             
  1164.             break;
  1165.         }
  1166.             
  1167.         case gxGetJobFormatLineConstraintQuery:            //    This type of query is not supported
  1168.             if (*(Handle *)dstData != nil)
  1169.                 SetHandleSize(*(Handle *)dstData, 0);        // Don't return any data
  1170.             break;
  1171.             
  1172.         case gxGetJobFormatFontsQuery:
  1173.         {
  1174.             short                numFonts;
  1175.             short                i;
  1176.             char                *pFontName;
  1177.  
  1178.             // Fetch the list of supported fonts
  1179.             
  1180.             anErr = Send_GXFetchTaggedDriverData('STR#', kFormatModeFontsID, &theFonts);
  1181.             require(anErr == noErr, FailedToLoadFonts);
  1182.             
  1183.             HNoPurge(theFonts);
  1184.             HLock(theFonts);
  1185.             
  1186.             // Determine the number of fonts in the list
  1187.             numFonts = **((short **) theFonts);
  1188.  
  1189.             if (*(Handle *)dstData != nil)    //    T => Resize the handle to hold info. on the fonts
  1190.                 SetHandleSize(*(Handle *)dstData, sizeof(gxFontTable) + ((numFonts - 1) * sizeof(gxFont)));
  1191.             else
  1192.                 *(Handle *)dstData = NewHandle(sizeof(gxFontTable) + ((numFonts - 1) * sizeof(gxFont)));
  1193.             
  1194.             anErr = MemError();
  1195.             require(anErr == noErr, FontTableResizeFailed);
  1196.             
  1197.             // Now generate a reference to each of the supported fonts
  1198.             
  1199.             for (i = 1, pFontName = ((char *) *theFonts) + sizeof(short); i <= numFonts; ++i, pFontName += *pFontName + 1)
  1200.             {
  1201.                 gxFont        thisFont;
  1202.                 gxFontTable    *pFontTable;
  1203.             
  1204.                 thisFont = FindPNameFont(gxFullFontName, pFontName);
  1205.                 
  1206.                 pFontTable = *((gxFontTableHdl) *(Handle *)dstData);
  1207.                 pFontTable->fonts[i - 1] = thisFont;
  1208.             }
  1209.             
  1210.             (*((gxFontTableHdl) *(Handle *)dstData))->numFonts = numFonts;
  1211.             
  1212.             // Dump the temporary handle
  1213.             DisposHandle(theFonts);
  1214.  
  1215.             break;
  1216.         }
  1217.             
  1218.         case gxGetJobFormatFontConstraintQuery:
  1219.         {
  1220.             gxPositionConstraintTable        *pPositionTable;
  1221.             
  1222.             if ( *(Handle *)dstData != nil)    //    T => Resize the handle to hold info. on position constraints
  1223.                 SetHandleSize(*(Handle *)dstData, sizeof(gxPositionConstraintTable) + sizeof(Fixed));
  1224.             else
  1225.                 *(Handle *)dstData = NewHandle( sizeof(gxPositionConstraintTable) + sizeof(Fixed) );
  1226.             
  1227.             pPositionTable = *((gxPositionConstraintTableHdl) *(Handle *)dstData);
  1228.             
  1229.             pPositionTable->phase.x     = 0;                //    Start at the top left corner of the page
  1230.             pPositionTable->phase.y     = 0;
  1231.             pPositionTable->offset.x     = ff(12);        // Indent from the top left by a six lines per inch margin
  1232.             pPositionTable->offset.y     = ff(12);         
  1233.             pPositionTable->numSizes     = 2;                // Two font sizes supported
  1234.             pPositionTable->sizes[0]     = ff(10);         // 10 pitch
  1235.             pPositionTable->sizes[1]     = ff(12);         // 12 pitch
  1236.             
  1237.             break;
  1238.         }
  1239.     } // switch
  1240.     
  1241.     return(anErr);
  1242.     
  1243.  
  1244. /******* Clean-up *******/
  1245.  
  1246. StyleTableResizeFailed:
  1247.     DisposHandle((Handle) theStyles);
  1248.     return(anErr);
  1249.  
  1250. FontTableResizeFailed:
  1251.     DisposHandle((Handle) theFonts);
  1252.  
  1253. FailedToLoadStyles1:
  1254. FailedToLoadStyles2:
  1255. FailedToLoadFonts:
  1256.     return(anErr);
  1257.     
  1258. } // SD_JobFormatModeQuery
  1259.  
  1260. //<FF>
  1261. /* ------------------------------------------------------------------------------------    */
  1262. OSErr SD_SetupImageData(
  1263.     gxRasterImageDataHdl hImageData)        // raster image data stuff
  1264. /*
  1265.     This message is called to setup the constant data used for imaging the entire job.
  1266. */
  1267. {
  1268.  
  1269.     OSErr                        anErr;
  1270.     gxRasterImageDataPtr        pImageData;
  1271.     Boolean                     isJobNotFinalQuality, isTextJobFormatMode;
  1272.     long                        imagewriterOptions;
  1273.     
  1274.     // do the default setup
  1275.     anErr = Forward_GXSetupImageData(hImageData);
  1276.     nrequire(anErr, Forward_GXSetupImageData);
  1277.     
  1278.     // test for 'final' quality mode
  1279.     isJobNotFinalQuality = !JobIsBest(&imagewriterOptions);
  1280.     
  1281.     // test for gxTextJobFormatMode
  1282.     isTextJobFormatMode = ( GXGetJobFormatMode( GXGetJob() ) == gxTextJobFormatMode);
  1283.             
  1284.     // if the job is not final quality or using gxTextJobFormatMode, downgrade the imaging data to our lower quality
  1285.     if (isJobNotFinalQuality  ||  isTextJobFormatMode)
  1286.         {
  1287.         // ROUGH OR TEXT MODE
  1288.         
  1289.         // dereference for size and speed    
  1290.         pImageData = *hImageData;
  1291.                 
  1292.         // image at 80 or 72 dpi
  1293.         if (imagewriterOptions & kSuperRes)
  1294.             pImageData->hImageRes = ff(80);
  1295.         else
  1296.             pImageData->hImageRes = ff(72);
  1297.         pImageData->vImageRes = ff(72);
  1298.         
  1299.         // gxTextJobFormatMode loads up the draft table, else setup halftones
  1300.         if (isTextJobFormatMode)
  1301.             {
  1302.             Handle            draftTable;
  1303.             SpecGlobalsHdl     hGlobals = GetMessageHandlerInstanceContext();
  1304.  
  1305.             anErr = Send_GXFetchTaggedDriverData('idft', gxPrintingTagID, &draftTable);
  1306.             nrequire(anErr, FailedToLoadDraftTable);
  1307.             
  1308.             // store away the draft table
  1309.             (**hGlobals).draftTable = draftTable;
  1310.             }
  1311.         else
  1312.             {
  1313.             short    plane;
  1314.             
  1315.             // use halftones that will look better at 72 dpi 
  1316.             // resolution than our default values.
  1317.             
  1318.             // we use the same angle and frequency for all planes, because gxDispersedDot
  1319.             // won't moire on us
  1320.             for (plane = 0; plane < 4; ++plane)
  1321.                 {
  1322.                 pImageData->theSetup.planeSetup[plane].planeHalftone.angle     = ff(0);
  1323.                 pImageData->theSetup.planeSetup[plane].planeHalftone.frequency = ff(9);
  1324.                 pImageData->theSetup.planeSetup[plane].planeHalftone.method = gxDispersedDot;
  1325.                 }
  1326.             }
  1327.             
  1328.         if (isJobNotFinalQuality)
  1329.             {
  1330.             if (imagewriterOptions & kSuperRes)
  1331.                 {
  1332.                 // use bidirectional instead of unidirectional
  1333.                 // and also <esc>N instead of <esc>p for quality mode
  1334.                 pImageData->packageControls.startPageStringID = gxPrintingDriverBaseID+3;
  1335.                 }
  1336.             else
  1337.                 {
  1338.                 // use bidirectional instead of unidirectional
  1339.                 // and also <esc>n instead of <esc>p for quality mode
  1340.                 pImageData->packageControls.startPageStringID = gxPrintingDriverBaseID+2;
  1341.                 }
  1342.             }
  1343.  
  1344.         // packaging data
  1345.         pImageData->packagingInfo.headHeight         = 8;        // 8 pins (instead of 16)
  1346.         pImageData->packagingInfo.numberPasses         = 1;        // in 1 head pass (instead of 2)
  1347.         pImageData->packagingInfo.passOffset         = 0;        // with no space between passes
  1348.         }
  1349.     else
  1350.         {
  1351.         // FINAL QUALITY
  1352.         
  1353.         // dereference for size and speed    
  1354.         pImageData = *hImageData;
  1355.                 
  1356.         // image at 160 or 144 dpi
  1357.         if (imagewriterOptions & kSuperRes)
  1358.             {
  1359.             pImageData->hImageRes = ff(160);
  1360.             pImageData->packageControls.startPageStringID = gxPrintingDriverBaseID+1;
  1361.             }
  1362.         else
  1363.             {
  1364.             pImageData->hImageRes = ff(144);
  1365.             pImageData->packageControls.startPageStringID = gxPrintingDriverBaseID+0;
  1366.             }
  1367.         }
  1368.     
  1369.     // not a color ribbon?  Setup for black and white
  1370.     if (!PrinterHasColorRibbon())
  1371.         {
  1372.         // dereference for size and speed    
  1373.         pImageData = *hImageData;
  1374.  
  1375.         // one plane, no color flags, move the halftone info up into correct position
  1376.         pImageData->theSetup.planes = 1;
  1377.         pImageData->packagingInfo.colorPasses = 1;
  1378.         pImageData->packagingInfo.packageOptions = 0;
  1379.         pImageData->theSetup.planeSetup[0] = pImageData->theSetup.planeSetup[3];
  1380.         pImageData->theSetup.planeSetup[0].planeHalftone.tinting = gxLuminanceTint;
  1381.         pImageData->theSetup.planeSetup[0].planeHalftone.tintSpace = gxRGBSpace;
  1382.         }
  1383.  
  1384.     
  1385. FailedToLoadDraftTable:
  1386. Forward_GXSetupImageData:
  1387.     return(anErr);
  1388.     
  1389. } // SD_SetupImageData
  1390.  
  1391. /* ------------------------------------------------------------------------------------    */
  1392. OSErr SD_FetchDriverData(
  1393.     ResType            theType,
  1394.     short            theID,
  1395.     Handle*            theData)
  1396. {
  1397.  
  1398.     OSErr    anErr;
  1399.     
  1400.     anErr = Forward_GXFetchTaggedDriverData(theType, theID, theData);
  1401.     
  1402.     // do the translation at the proper DPI by modifying the old API
  1403.     // customization resource
  1404.     if ( (anErr   == noErr)    &&                 // got the resource okay
  1405.          (theType == 'cust')   &&                // it was a customization resource 
  1406.          (theID   == -8192)   )                    // with the old API id
  1407.         {
  1408.         long imagewriterOptions;
  1409.         
  1410.         if (!JobIsBest(&imagewriterOptions))
  1411.             {
  1412.             **((short**)theData)   = 72;
  1413.             **((short**)theData+1) = 72;
  1414.             }
  1415.         }
  1416.         
  1417.     return(anErr);
  1418.     
  1419. } // SD_FetchDriverData
  1420.  
  1421.  
  1422. /* ------------------------------------------------------------------------------------    */
  1423. OSErr SD_RenderPage(    gxFormat                theFormat,
  1424.                         gxShape                    thePage,
  1425.                         gxPageInfoRecord        *pageInfo,
  1426.                         gxRasterImageDataHdl    imageInfo)
  1427. /*
  1428.     The message sent to render an entire page.
  1429. */
  1430. {
  1431.  
  1432.     OSErr    theError = noErr;
  1433.  
  1434.     // if not text mode, do it the normal (raster) way
  1435.     if (GXGetJobFormatMode(GXGetJob()) != gxTextJobFormatMode) 
  1436.         {
  1437.         gxRectangle            paperSize;
  1438.         Str63                formLength;            // should be more than big enough for form skipping
  1439.         char                aNumber[8];
  1440.         char                len = 0;
  1441.         short                formLen;            // form length (in 144 dpi)
  1442.         short                i;
  1443.         
  1444.         
  1445.         // find out how big our paper is
  1446.         GXGetPaperTypeDimensions(GXGetFormatPaperType(theFormat), nil, &paperSize);
  1447.         
  1448.         // determine the left margin (in pixels)
  1449.         {
  1450.         SpecGlobalsHdl             hGlobals = GetMessageHandlerInstanceContext();
  1451.         SpecGlobalsPtr            pGlobals;
  1452.         gxRasterImageDataPtr    pImageData;
  1453.  
  1454.         check(hGlobals);
  1455.  
  1456.         // dereference for size and speed    
  1457.         pImageData     = *imageInfo;
  1458.         pGlobals = *hGlobals;
  1459.         paperSize.left += ff(18);        // ImageWriter's can't go tighter than .25 inch
  1460.         if (paperSize.left > 0)
  1461.             paperSize.left = 0;
  1462.         pGlobals->leftMargin     = FixedToInt(
  1463.                                     FixMul(-paperSize.left, 
  1464.                                         FixDiv(pImageData->hImageRes, ff(72))));
  1465.         }
  1466.         
  1467.         // move over the top margin, set the form length to be the size of the page
  1468.         
  1469.         // form length == top margin
  1470.         formLen = FixedToInt(FixMul(-paperSize.top, ff(2)) );                    // length is set in 144 dpi
  1471.         if (formLen > 0)
  1472.             {
  1473.             // send multiples of 99
  1474.             if (formLen >= 99)
  1475.                 {
  1476.                 formLength[len++] = ESCAPE;
  1477.                 formLength[len++] = 'T';
  1478.                 formLength[len++] = '9';
  1479.                 formLength[len++] = '9';
  1480.                 while (formLen >= 99)
  1481.                     {
  1482.                     formLength[len++] = 0x0A;        // line feed
  1483.                     
  1484.                     formLen -= 99;
  1485.                     }
  1486.                 }
  1487.                 
  1488.             // send remaining line feeds
  1489.             if (formLen > 0)
  1490.                 {
  1491.                 formLength[len++] = ESCAPE;
  1492.                 formLength[len++] = 'T';
  1493.                 NumToString(formLen, aNumber);
  1494.                 if (aNumber[0] == 1)
  1495.                     {
  1496.                     formLength[len++] = '0';
  1497.                     formLength[len++] = aNumber[1];
  1498.                     }
  1499.                 else
  1500.                     {
  1501.                     formLength[len++] = aNumber[1];
  1502.                     formLength[len++] = aNumber[2];
  1503.                     }
  1504.                 formLength[len++] = 0x0A;        // line feed
  1505.                 }
  1506.             }
  1507.         
  1508.         // form length == page size
  1509.         formLength[len++] = ESCAPE;
  1510.         formLength[len++] = 'H';
  1511.         formLen = FixedToInt(FixMul(paperSize.bottom-paperSize.top, ff(2)) );    // length is set in 144 dpi
  1512.         NumToString(formLen, aNumber);
  1513.         for (i = 0; i < 4-aNumber[0]; ++i)
  1514.             formLength[len++] = '0';
  1515.         for (i = 1; i <= aNumber[0]; ++i)
  1516.             formLength[len++] = aNumber[i];
  1517.  
  1518.         // we've got all of this data, now send it
  1519.         theError = Send_GXBufferData(&formLength[0], len, gxNoBufferOptions );
  1520.         nrequire(theError, SetFormLength);        
  1521.                 
  1522.         // continue with normal rendering
  1523.         theError = Forward_GXRenderPage(theFormat, thePage, pageInfo, imageInfo);
  1524.         } 
  1525.     else 
  1526.         {
  1527.         theError = PrintPageInDraftMode(thePage, imageInfo);
  1528.         }
  1529.  
  1530. failed_WrongShape:
  1531. SetFormLength:
  1532.     return(theError);
  1533.     
  1534. } // SD_RenderPage
  1535.  
  1536.  
  1537. //<FF>
  1538. /* ------------------------------------------------------------------------------------    */
  1539. /*    SPECIFIC DRIVER RASTER OVERRIDES                                                    */
  1540. /* ------------------------------------------------------------------------------------    */
  1541. OSErr SD_LineFeed (
  1542.     short                 * lineFeedSize, // amount to line feed by
  1543.     Ptr buffer, unsigned long * bufferPos,     // data goes here
  1544.     gxRasterImageDataHdl hImageData)        // raster image data stuff
  1545. /*
  1546.     Message is sent to output paper advance commands to the printer
  1547. */
  1548. {
  1549.  
  1550.     OSErr    anErr;
  1551.     Boolean    amLowRes;
  1552.     
  1553.     amLowRes = ((**hImageData).vImageRes == ff(72));
  1554.     // if we are in low res mode, we double the line feed size, as all ImageWriter 
  1555.     // line feed commands are expressed at 144 dpi.
  1556.     if (amLowRes)
  1557.         *lineFeedSize <<= 1;
  1558.         
  1559.     // do the line feed in the default way
  1560.     anErr = Forward_GXRasterLineFeed(lineFeedSize, buffer, bufferPos, hImageData);
  1561.     
  1562.     // and if in low quality mode, we divide the result to make up for the multiplication
  1563.     // that we do above
  1564.     if (amLowRes)
  1565.         *lineFeedSize >>= 1;    
  1566.         
  1567.     return(anErr);
  1568.     
  1569. } // SD_LineFeed
  1570.  
  1571. //<FF>
  1572. /* ------------------------------------------------------------------------------------    */
  1573. OSErr SD_PackageBitmap (
  1574.     gxRasterPackageBitmapRec    *pPackage,
  1575.     Ptr                         buffer,     // data goes here + bufferPos
  1576.     unsigned long                 *bufferPos,    // how much of the buffer already is full
  1577.     gxRasterImageDataHdl         hImageData)    // private image data
  1578. /*
  1579.     Packages a bitmap for the ImageWriter
  1580.     This routine is called in order to add your rotated and packaged pixel
  1581.     data to the buffer.  It is called once for each head pass.  This routine
  1582.     is pretty complex because it also does IW run length compression.  
  1583.     
  1584.     It must do the following:
  1585.         
  1586.     1)    Start filling the buffer from buffer+bufferPos.  Remember
  1587.         that this pointer may not be word aligned - so be careful
  1588.         assigning things into it.
  1589.         
  1590.     2)    If your printer does SetMargins, put a "fake" set of commands at
  1591.         the begining of the data.  Since most of the time you don't
  1592.         know the margins, you can save away the value of the bufferPos,
  1593.         and backpatch it after you have finished with the offscreen.
  1594.         SetMargins is used on printers that allow you to not send starting
  1595.         and ending whitespace.
  1596.         
  1597.     3)    Add in the rotated data for your printer.  The data to stuff starts
  1598.         at location startY in hOffscreen.  Stuff the bits from here until
  1599.         you reach startY+<your band size>, which is the size of your single
  1600.         band in this resolution mode.  Increment your number by 
  1601.         <your pass offset> + 1, which is the number of microspaces
  1602.         you will send between head passes to form this band.   Take care
  1603.         that you don't step off of the end of the offscreen in this operation,
  1604.         you may be called with startY at the end of the offscreen if the first part
  1605.         of the band is white.
  1606.         
  1607.         colorBand contains the color band which you should be stuffing, from
  1608.         1 to the number of color passes your printer needs (usually 4).
  1609.         Pack in the correct color band.  The packager will call you once
  1610.         for each color band, and correctly handle line feeds and backward
  1611.         line feeds to do the correct thing.  If your printer takes full
  1612.         RGB or CYMK data, I would define your number of colors to be
  1613.         1 and pack the full RGB or CYMK data with the one call to StuffBuffers.
  1614.         
  1615.         If you request kSendAllColors in your raster pack resource, then this
  1616.         message will always be called for all color passes, even those that
  1617.         do not have data on them.  For some printers, this is useful.  For the
  1618.         case of the ImageWriter, this option is not specified, so this message
  1619.         will only be sent for colors that actually have dirty bits within them.
  1620.         
  1621.     4)    Backpatch SetMargins from your saved value in step 2) now that you
  1622.         know the margins.
  1623.         
  1624.     5)    Increment bufferPos by the number of bytes you have
  1625.         added to the buffer.  Be sure to take into account the Set Margins
  1626.         command if you added one.
  1627.  
  1628.     
  1629. */
  1630. {
  1631. #pragma unused (isColorDirty)
  1632.  
  1633.     ScanLinePtr        pTheScanLine;            // Pointer to the start of scan line data
  1634.     unsigned short            lastDirtyCol;            // Last dirty part of the scan line
  1635.     unsigned short            firstDirty;                // First dirty pixel
  1636.     unsigned short            lastDirty;                // Last dirty pixel
  1637.     Boolean            bandIsDirty;            // Is this band dirty?
  1638.     unsigned short            numberBytesAdded;        // Number of bytes we have added to the row
  1639.     unsigned short            repeatCount;            // Number of times we have seen this bitmap
  1640.     
  1641.     register unsigned short    whichCol;                // Index into the scan line data
  1642.     register unsigned short    x,y;                    // Index values into the offscreen
  1643.         
  1644.     register Ptr    thePtr;                    // Pointer to each Y scanline
  1645.     register unsigned char    tempColumn;                // Placeholder for the working column
  1646.     unsigned char            lastColumn;                // What was in the contents of the last column?
  1647.     
  1648.     Ptr                basePtr;                // Pointer to current X byte
  1649.     unsigned char            outputMask;                // Mask of bit to set in rotated image
  1650.     unsigned char            inputMask;                // Mask of bit to look at in X
  1651.     unsigned short            yPointerOffset;            // Increment pointer by this to get to next scanline
  1652.     
  1653.     unsigned short            endY, endX, incrY;        // To remove loop invariants.
  1654.             
  1655.     
  1656. /* This macro stores one group into the pointer:
  1657.     P = Pointer to fill into
  1658.     G = Character for group
  1659.     S = Length of group run in pixels
  1660. */
  1661. #define EMITGROUP(P, G, S)                    \
  1662.         P->cEscape = ESCAPE;                \
  1663.         P->cCommand = G;                    \
  1664.         Long2Dec(S, P->cLineLength);        
  1665.  
  1666.     pTheScanLine = (ScanLinePtr) (buffer + kSetMarginsSize + (*bufferPos));
  1667.     
  1668.     /* Set color mode for this scan line, if needed */
  1669.     pTheScanLine->cColorEscape        = ESCAPE;
  1670.     pTheScanLine->cSetColorCommand    = kSetColorCommand;
  1671.     
  1672.     if ((*hImageData)->packagingInfo.colorPasses == 4)
  1673.         switch (pPackage->colorBand)
  1674.             {
  1675.             case 1: // yellow
  1676.                 pTheScanLine->cColor    = '1';
  1677.                 break;
  1678.                 
  1679.             case 2: // magenta
  1680.                 pTheScanLine->cColor    = '2';
  1681.                 break;
  1682.  
  1683.             case 3: // cyan
  1684.                 pTheScanLine->cColor    = '3';
  1685.                 break;
  1686.                 
  1687.             case 4: // black
  1688.                 pTheScanLine->cColor    = '0';
  1689.                 break;
  1690.                 
  1691.             }
  1692.     else
  1693.         pTheScanLine->cColor = '0';
  1694.  
  1695.     /* We start out with no dirty bits */
  1696.     firstDirty = 0;
  1697.     lastDirty = 0;
  1698.     bandIsDirty = false;
  1699.     
  1700.     /* Set our array index to zero */
  1701.     whichCol = 0;
  1702.     lastDirtyCol = 0;
  1703.     numberBytesAdded = 0;
  1704.     
  1705.     /* Set up RLL variables */
  1706.     repeatCount = 0;
  1707.     lastColumn = 0;
  1708.     
  1709.     /* Get the byte pointer for the start of this color band */
  1710.     basePtr = pPackage->bitmapToPackage->image;
  1711.     
  1712.     /* Get the byte pointer for the start of the first scan line */ 
  1713.     basePtr += pPackage->startRaster * pPackage->bitmapToPackage->rowBytes;
  1714.             
  1715.     /* Start with the first bit in the offscreen */
  1716.     inputMask = 0x80;
  1717.  
  1718.     /* Save away loop invariants */
  1719.     endY     = pPackage->startRaster + (*hImageData)->packagingInfo.headHeight;        // Ending scan line
  1720.     incrY     = (*hImageData)->packagingInfo.passOffset + 1;            // Number of scanlines to increment by
  1721.     endX     = pPackage->dirtyRect.right;                                // Ending X pos
  1722.     yPointerOffset = incrY * pPackage->bitmapToPackage->rowBytes;            // amount to add to the input
  1723.                                                             // pointer to move to the next scanline
  1724.  
  1725.     /* If the ending position is too large for the bitmap we have been given,
  1726.        truncate it, so that we don't print garbage */
  1727.     if (endY > pPackage->bitmapToPackage->height)
  1728.         endY = pPackage->bitmapToPackage->height;
  1729.     
  1730.     /* For the entire width of the offscreen, move a rolling mask along in the
  1731.        X direction, rotating up 8 bits of Y data per column.  In addition, compress
  1732.        runs of columns that are > 14 length. */
  1733.     for (x = 0; x < endX; x++)
  1734.         {        
  1735.         /* The bits in this column are clear to begin with */
  1736.         tempColumn = 0;
  1737.         
  1738.         /* Which byte to look at in the input buffer */
  1739.         thePtr = basePtr;
  1740.         
  1741.         /*     Where to place the bit in the output. The ImageWriter takes the bit
  1742.             pattern upside down. */
  1743.         outputMask = 0x01;
  1744.         
  1745.         /* Scan through this band, setting each of the 8 bits == the bit in that scan line */
  1746.         for (y = pPackage->startRaster; y < endY; y += incrY)
  1747.             {
  1748.             /* If we have a bit in the input, rotate it into the output */
  1749.             if ((*thePtr) & inputMask)
  1750.                 tempColumn |= outputMask;
  1751.  
  1752.             // move onto next position in the output data                
  1753.             outputMask <<= 1;
  1754.             
  1755.             // move onto the next scan line in the input data
  1756.             thePtr += yPointerOffset;
  1757.             } // for y
  1758.             
  1759.             
  1760.         /* Save the column info */ 
  1761.         pTheScanLine->iTheData[whichCol] = tempColumn;
  1762.         
  1763.         /* Get the next bit from the current pointer */
  1764.         inputMask >>= 1;
  1765.         if (!inputMask)
  1766.             {
  1767.             /* If we run out of bits, get the next byte */
  1768.             basePtr++;
  1769.             
  1770.             /* And reset the bit mask to the first bit */
  1771.             inputMask = 0x80;
  1772.             }
  1773.             
  1774.         /* If we have some form of data */
  1775.         if (tempColumn != 0)
  1776.             {
  1777.             if (!bandIsDirty)
  1778.                 {
  1779.                 /* This is the first dirty pixels we have so far */
  1780.                 bandIsDirty = true;
  1781.                 firstDirty = x;
  1782.                 }
  1783.             
  1784.             /* This is also the last dirty pixels so far */
  1785.             lastDirty = x;
  1786.             } // SetDirty
  1787.             
  1788.         /* If we have some dirty bits */
  1789.         if (bandIsDirty)
  1790.             {
  1791.             /* Move on to the next column */
  1792.             whichCol++;
  1793.             
  1794.             /* If this is a dirty column, then it is the last one so far */
  1795.             if (tempColumn != 0)
  1796.                 lastDirtyCol = whichCol;
  1797.             
  1798.             /* If we have a duplication, up the repeat count */
  1799.             if (tempColumn == lastColumn) // if (false) // turn off repeat groups
  1800.                 {
  1801.                 repeatCount++;
  1802.                 if (repeatCount == 14)
  1803.                     {
  1804.                     /* Kick out the old group */
  1805.                     whichCol -= 14;
  1806.                         EMITGROUP(pTheScanLine, kGraphicsCommand, whichCol);
  1807.                         numberBytesAdded += whichCol + kGroupSize;
  1808.                         pTheScanLine = (ScanLinePtr)(((Ptr) pTheScanLine) +
  1809.                             whichCol + kGroupSize);
  1810.                     
  1811.                     whichCol = 1;
  1812.                     lastDirtyCol = 1;
  1813.                     pTheScanLine->iTheData[0] = tempColumn;
  1814.                     }
  1815.                 }
  1816.             else
  1817.                 {
  1818.                 /* If we were repeating, emit the repeat group */
  1819.                 if (repeatCount >= 14)
  1820.                     {
  1821.                     EMITGROUP(pTheScanLine, kRepeatGroup, repeatCount);
  1822.                     numberBytesAdded += 1 + kGroupSize;
  1823.                     pTheScanLine = (ScanLinePtr) (((Ptr) pTheScanLine) + 
  1824.                         1 + kGroupSize);
  1825.                         
  1826.                     whichCol = 1;
  1827.                     lastDirtyCol = 1;
  1828.                     pTheScanLine->iTheData[0] = tempColumn;
  1829.                     }
  1830.                 repeatCount = 0;
  1831.                 lastColumn = tempColumn;
  1832.                 }
  1833.                 
  1834.             } // BandIsDirty
  1835.             
  1836.         } // end of loop for width of bitmap
  1837.  
  1838.     /* if we have a dirty band - emit the final bit of data we have
  1839.        packaged up */
  1840.     if (bandIsDirty)
  1841.         {            
  1842.         
  1843.         /* Set the margins to be the first and last dirty pixels in the scan line -
  1844.            the ImageWriter only does left margin optimization. */
  1845.         {
  1846.             SetMarginsPtr        marginBuffer;
  1847.             SpecGlobalsHdl         hGlobals = GetMessageHandlerInstanceContext();
  1848.             
  1849.             check(hGlobals);
  1850.             
  1851.             /* Get the location for placing the set margin command */
  1852.             marginBuffer = (SetMarginsPtr) (buffer + (*bufferPos));
  1853.             
  1854.             /* Stuff in the set margin command */
  1855.             marginBuffer->cEscape  = ESCAPE;
  1856.             marginBuffer->cCommand = kSetMarginsCommand;
  1857.             
  1858.             /* convert left margin into ASCII and place it at the start of the buffer */
  1859.             Long2Dec((**hGlobals).leftMargin + firstDirty, (Ptr)(marginBuffer->cIndentDistance));
  1860.         }
  1861.         
  1862.         /* Send the last group command */
  1863.         if (repeatCount < 14)
  1864.             {
  1865.             /* Emit a normal group */
  1866.             EMITGROUP(pTheScanLine, kGraphicsCommand, lastDirtyCol);
  1867.             numberBytesAdded += lastDirtyCol + kGroupSize;
  1868.             }
  1869.         else
  1870.             {
  1871.             /* Don't stuff a final repeat group if it's blank space */
  1872.             if (tempColumn != 0)
  1873.                 {
  1874.                 /* Emit a repeat group */
  1875.                 EMITGROUP(pTheScanLine, kRepeatGroup, repeatCount);
  1876.                 numberBytesAdded += 1 + kGroupSize;
  1877.                 }
  1878.             } // end of repeatCount < 14
  1879.                     
  1880.         
  1881.         /*    Increment the count of the buffer by bytes added for groups, plus
  1882.             the header, if any, plus the set margins command */
  1883.         (*bufferPos) += numberBytesAdded + kScanLineSize + kSetMarginsSize;
  1884.  
  1885.         /* and put a <cr> at the end of the line */
  1886.         *(char*)(buffer + (*bufferPos)) = '\n';
  1887.         (*bufferPos) += 1;
  1888.         
  1889.         } // bandIsDirty
  1890.         
  1891.     // amazingly enough, this routine can't have any errors
  1892.     return(noErr);
  1893.     
  1894. } // SD_PackageBitmap
  1895.